﻿using Crawler.Core.EventBus;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Crawler.Framework.EventBus
{
    public class DefualtEventBus : IEventBus
    {

        private Dictionary<Type, List<Type>> dicHandlers;
        private Dictionary<Type, object> dicHandlerInstances;
        private readonly IServiceProvider service;
        private Dictionary<Type, ConcurrentQueue<IEventEntity>> dicEventQueue;

        public DefualtEventBus(IServiceProvider service)
        {
            this.service = service;
            dicHandlers = new Dictionary<Type, List<Type>>();
            dicHandlerInstances = new Dictionary<Type, object>();
            dicEventQueue = new Dictionary<Type, ConcurrentQueue<IEventEntity>>();
        }

        /// <summary>
        /// 触发事件
        /// </summary>
        /// <typeparam name="EventEntity"></typeparam>
        /// <param name="t"></param>
        private void PublishEvent<EventEntity>(EventEntity t) where EventEntity : IEventEntity
        {
            if (!dicHandlers.ContainsKey(t.GetType())) return;
            var handlers = dicHandlers[t.GetType()];
            handlers?.ForEach(handlerType =>
            {
                try
                {
                    if (!dicHandlerInstances.ContainsKey(handlerType))
                    {
                        lock ("dicHandlerInstances")
                        {
                            if (!dicHandlerInstances.ContainsKey(handlerType))
                            {
                                var handler = service.GetService(handlerType);
                                dicHandlerInstances.Add(handlerType, handler);
                            }
                        }
                    }
                    handlerType.GetMethod("Trigger")
                    .Invoke(dicHandlerInstances[handlerType], new object[] { t });
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            });
        }
        /// <summary>
        /// 发布
        /// </summary>
        /// <typeparam name="EventEntity"></typeparam>
        /// <param name="t"></param>
        public void Publish<EventEntity>(EventEntity t) where EventEntity : IEventEntity
        {
            Task.Factory.StartNew(obj =>
            {
                try
                {
                    var type = obj.GetType();
                    if (!dicEventQueue.ContainsKey(type))
                    {
                        lock ("DefualtEventBus-Publish")
                        {
                            if (!dicEventQueue.ContainsKey(type))
                            {
                                var _queue = new ConcurrentQueue<IEventEntity>();
                                dicEventQueue.Add(type, _queue);

                                Task.Factory.StartNew(qobj =>
                                {
                                    var eventQueue = qobj as ConcurrentQueue<IEventEntity>;
                                    while (true)
                                    {
                                        if (!eventQueue.IsEmpty)
                                        {
                                            IEventEntity entity = null;
                                            while (eventQueue.TryDequeue(out entity))
                                            {
                                                try
                                                {
                                                    PublishEvent(entity);
                                                }
                                                catch (Exception ex)
                                                {
                                                    Console.WriteLine(ex.ToString());
                                                }
                                            }
                                        }
                                    }
                                }, _queue);

                            }
                        }
                    }
                    dicEventQueue[type].Enqueue((EventEntity)obj);//将事件插入到队列
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }

            }, t);
        }
        /// <summary>
        /// 订阅
        /// </summary>
        /// <typeparam name="EventHandler"></typeparam>
        /// <typeparam name="EventEntity"></typeparam>
        public void Subscribe<EventHandler, EventEntity>()
            where EventHandler : IEventHandler<EventEntity>
            where EventEntity : IEventEntity
        {
            var type = typeof(EventEntity);
            if (!dicHandlers.ContainsKey(type))
            {
                lock ("addEventHandler")
                {
                    if (!dicHandlers.ContainsKey(type))
                    {
                        dicHandlers.Add(type, new List<Type>());
                    }
                }
            }
            dicHandlers[type].Add(typeof(EventHandler));
        }
    }
}
